﻿/**
 \file table.c
 Исходный код модуля работы с таблицами символов.
*/
#include "common.h"
#include "table.h"
#include "scanner.h"
#include <string.h>

/**
 \var struct Level *CurLevel
 Указатель на основую таблицу символов.
 \var struct Level *Modules
 Указатель на таблицу символов импортируемых модулей.
 \var struct Item  *PredefinedScope
 Указатель на список предопределенных идентификаторов |
 Наличие такого указателя убыстряет поиск.
*/
struct Level *CurLevel = NULL;
struct Level *Modules = NULL;
struct Item  *PredefinedScope = NULL;

/**
 \fn struct Level *GetPredScope(void)
 Возращает указатель на список предопределенных типов и процедур.
*/
struct Item *GetPredScope(void)
{
 return PredefinedScope;
}

/**
 \fn void SetPredScope(struct Item *item)
 Присваивает значение PredefinedScope.
 \param item Указатель на список с предопределенными типвми и процедурами.
*/
void SetPredScope(struct Item *item)
{
 PredefinedScope = item;
}

/**
 \fn struct Level *GetCurLevel(void)
 Возращает указатель на текущий уровень основной таблицы символов.
*/
struct Level *GetCurLevel(void)
{
 return CurLevel;
}

/**
 \fn struct Level *GetModules(void)
 Возращает указатель на текущий уровень таблицы внешних модулей.
*/
struct Level *GetModules(void)
{
 return Modules;
}

/**
 \fn void FillItem(struct Item *item, unsigned char cls,
                   unsigned char type,unsigned char scope, 
                   unsigned char flags, unsigned long int size, 
                   union Data one, struct Item *child)
 Заполняет переменную-структуру типа Item.
 \param item Указатель на заполняемую структуру.
 \param cls Класс элемента.
 \param type Тип элемента.
 \param scope Область видимости элемента (глобальный или локальный)
 \param flags Используется для обозначения открытого или закрытого элемента.
              Так же применяется для указания типа формального параметра.
 \param size Размер элемента.
 \param one Используется для хранения различных данных: значений констант, указателей
            на родительские элементы.
 \param child Указатель на дочерний элемент.
*/
void FillItem(struct Item *item, unsigned char cls,
              unsigned char type,unsigned char scope, 
              unsigned char flags, unsigned long int size, 
              union Data one, struct Item *child)
{
 item->cls = cls;
 item->type = type;
 item->scope = scope;
 item->flags = flags;
 item->size = size;
 item->one = one;
 item->child = child;
}

/**
 \fn void DelItems(struct Item *item)
 Удаляет все элементы на одном уровне таблицы символов.
 \param item Указатель на список элементов, который нужно удалить.
*/
void DelItems(struct Item *item)
{
 struct Item *current = NULL, *next = NULL;

 current = item;
 do {
  if (current->name != NULL)
   free(current->name);
  /*switch(current->type) {
   case tpString: {
    if (current->one.sval != NULL)
     free(current->one.sval);
    break;
   }
   case tpInt: {
    if (current->one.lval != NULL)
     free(current->one.lval);
    break;
   }
   default:
    current->one.parent = NULL;
  };*/
  current = next;
  free(current);
  if (current != next)
   next = current->next;
 } while (current != next);
 free(current);
}

/**
 \fn struct Item *SearchItem(struct Item *first, char *name)
 Ищет элемент на одном уровне таблицы символов по имени.
 \param first Указатель на список элементов в которых ищется нужный.
 \param name Имя искомого элемента таблицы.
 \return Если поиск прошел успешно, возвращается указатель на элемент. В противном
         случае возвращается NULL.
*/
struct Item *SearchItem(struct Item *first, char *name)
{
 struct Item *current = NULL;

 if (first == NULL)
  return NULL;
 current = first->next;
 while (current != first) {
  if (!strcmp((const char *)current->name,(const char *)name))
   return current;
  current = current->next;
 }
 return NULL;
}

/**
 \fn struct Item *Search(char *name, char cls)
 Ищет элемент во всей таблице символов по его имени и классу.
 \param name Имя искомого элемента.
 \param cls Его класс.
 \return Если поиск прошел успешно, возвращается указатель на элемент. В противном
         случае возвращается NULL.
*/
struct Item *Search(char *name, char cls)
{
 struct Level *current = NULL, *next = NULL;
 struct Item *tmp = NULL;

 current = CurLevel;
 if (CurLevel != NULL)
  next = CurLevel->next;
 while (current != NULL) {
  tmp = SearchItem(current->items,name);
  if (tmp != NULL) {
   if (cls == cNone)
    return tmp;
   if (tmp->cls == cls)
    return tmp;
  }
  current = next;
  if (current != NULL)
   next = current->next;
 }
 return NULL;
}

/**
 \fn struct Item *AddItem(struct Item *first, char *name)
 Добавлет новый элемент к указанному списку.
 \param first Указатель на первый элемент списка.
 \param name Имя элемента.
 \return Возвращает указатель на созданный элемент.
*/
struct Item *AddItem(struct Item *first, char *name)
{
 struct Item *temp = NULL, *it = NULL;
 unsigned long int len = 0;
 union Data dat;
 
 temp = SearchItem(first,name);
 if (temp == NULL)
  temp = SearchItem(PredefinedScope,name);
 if (temp != NULL) {
  printf(ETEMPLATE,GetCurLine(),eIdentDeclared(name));
  exit(1);
 }
 temp = (struct Item *)GetMem(sizeof(struct Item),"AddItem");
 len = strlen(name);
 temp->name = (unsigned char *)GetMem(len+1,"AddItem");
 strcpy((char *)temp->name,(const char *)name);
 temp->name[len] = 0;
 dat.parent = NULL;
 FillItem(temp,cNone,0,0,0,0,dat,NULL);
 temp->next = first;
 if (first->child != NULL)
  first->child->next = temp;
 first->child = temp;
 if (first->next == first)
  first->next = temp;
 return temp;
}

/**
 \fn void DeleteLevel(void)
 Удаляет текущий уровень таблицы символов.
*/
void DeleteLevel(void)
{
 struct Level *temp = NULL;

 if (CurLevel != NULL) {
  if (CurLevel->items != NULL)
   DelItems(CurLevel->items);
  temp = CurLevel->next;
  free(CurLevel);
  CurLevel = temp;
 }
}

/**
 \fn void AddLevel(unsigned char TableType)
 Добавляет новый уровень в указанную таблицу символов.
 \param TableType Куда нужно добавить элемент: GENERAL_TABLE - в главную таблицу,
                  MODULES_TABLE - в таблицу символов для внешних иодулей.
*/
void AddLevel(unsigned char TableType)
{
 struct Level *temp = NULL;
 struct Item *item = NULL; 
 union Data one;

 temp = (struct Level *)GetMem(sizeof(struct Level),"AddLevel");
 temp->items = NULL;
 if (TableType == GENERAL_TABLE)
 {
  temp->next = CurLevel;
  CurLevel = temp;
 }
 if (TableType == MODULES_TABLE)
 {
  temp->next = Modules;
  Modules = temp;
 }
 item = (struct Item *)GetMem(sizeof(struct Item),"AddLevel");
 item->name = (unsigned char *)GetMem(2,"AddLevel");
 item->name[0] = '.';
 item->name[1] = 0;
 item->next = NULL;
 if (TableType == GENERAL_TABLE)
  CurLevel->items = item;
 if (TableType == MODULES_TABLE)
  Modules->items = item;
 one.parent = NULL;
 FillItem(item,cNone,0,LOCAL,0,0,one,NULL);
 item->next = item;
}
